/*
 * @Author: your name
 * @Date: 2021-09-22 16:42:43
 * @LastEditTime: 2021-10-14 18:20:22
 * @LastEditors: Please set LastEditors
 * @Description: In User Settings Edit
 * @FilePath: \web-maps\src\util\colorUtil.ts
 */
import * as d3 from "d3";

// enum ColorType {
//   RGB,
//   HSL,
//   HCl,
// }

// interface IRGBColor extends BaseColor {
//   r: number;
//   g: number;
//   b: number;
// }
// interface IHSLColor extends BaseColor {
//   h: number;
//   s: number;
//   l: number;
// }
// interface IHCLColor extends BaseColor {
//   h: number;
//   c: number;
//   l: number;
// }

// interface BaseColor {
//   opacity?: number;
// }

// type Color = IRGBColor | IHSLColor | IHCLColor;

// type ReturnColor = RGBColor | HSLColor | HCLColor;

// type ColorOpt = {
//   range: { min: number; max: number };
//   type: ColorType;
//   colorRange: { startColor: Color; endColor: Color };
// };

interface ILegend {
  id?: string;
  color: any;
  title: string;
  tickSize?: number;
  width?: number;
  height?: number;
  marginTop?: number;
  marginRight?: number;
  marginBottom?: number;
  marginLeft?: number;
  ticks?: number;
  tickFormat?: any;
  tickValues?: any;
}

// const createRGB = (color: IRGBColor) =>
//   rgb(color.r, color.g, color.b, color.opacity);

// const createHSL = (color: IHSLColor) =>
//   hsl(color.h, color.s, color.l, color.opacity);

// const createHCL = (color: IHCLColor) =>
//   hcl(color.h, color.c, color.l, color.opacity);

// const ColorSelector = {
//   [ColorType.RGB]: createRGB,
//   [ColorType.HSL]: createHSL,
//   [ColorType.HCl]: createHCL,
// };

// export const createColors = (opt: ColorOpt) => {
//   const {
//     type,
//     colorRange: { startColor, endColor },
//     range: { min, max },
//   } = opt;
//   let _startColor: ReturnColor, _endColor: ReturnColor;
//   switch (type) {
//     case ColorType.RGB:
//       _startColor = createRGB(startColor as IRGBColor);
//       _endColor = createRGB(endColor as IRGBColor);
//       break;
//     case ColorType.HSL:
//       _startColor = createHSL(startColor as IHSLColor);
//       _endColor = createHSL(endColor as IHSLColor);
//     case ColorType.HCl:
//       _startColor = createHCL(startColor as IHCLColor);
//       _endColor = createHCL(endColor as IHCLColor);
//       break;
//   }
// };

/**
 * @description 调色板
 * @see https://stackoverflow.com/questions/64803258/import-legend-from-d3-color-legend-what-is-this-in-a-html-code-is-there-a
 * @param  {ILegend} opt
 * @returns
 */
function legend({
  id = "#palette",
  color,
  title,
  tickSize = 6,
  width = 300,
  height = 45 + tickSize,
  marginTop = 18,
  marginRight = 0,
  marginBottom = 16 + tickSize,
  marginLeft = 0,
  ticks = width / 64,
  tickFormat,
  tickValues,
}: ILegend) {
  removeLegend();

  const svg = d3
    .select(id)
    .attr("width", width)
    .attr("height", height)
    .attr("viewBox", [0, 0, width, height].join(","))
    .style("overflow", "visible")
    .style("display", "block");

  let tickAdjust = (g:any) =>
    g.selectAll(".tick line").attr("y1", marginTop + marginBottom - height);
  let x:any;

  // Continuous
  if (color.interpolate) {
    const n = Math.min(color.domain().length, color.range().length);

    x = color
      .copy()
      .rangeRound(
        d3.quantize(d3.interpolate(marginLeft, width - marginRight), n)
      );

    svg
      .append("image")
      .attr("x", marginLeft)
      .attr("y", marginTop)
      .attr("width", width - marginLeft - marginRight)
      .attr("height", height - marginTop - marginBottom)
      .attr("preserveAspectRatio", "none")
      .attr(
        "xlink:href",
        ramp(
          color.copy().domain(d3.quantize(d3.interpolate(0, 1), n))
        ).toDataURL()
      );
  }

  // Sequential
  else if (color.interpolator) {
    x = Object.assign(
      color
        .copy()
        .interpolator(d3.interpolateRound(marginLeft, width - marginRight)),
      {
        range() {
          return [marginLeft, width - marginRight];
        },
      }
    );

    svg
      .append("image")
      .attr("x", marginLeft)
      .attr("y", marginTop)
      .attr("width", width - marginLeft - marginRight)
      .attr("height", height - marginTop - marginBottom)
      .attr("preserveAspectRatio", "none")
      .attr("xlink:href", ramp(color.interpolator()).toDataURL());

    // scaleSequentialQuantile doesn’t implement ticks or tickFormat.
    if (!x.ticks) {
      if (tickValues === undefined) {
        const n = Math.round(ticks + 1);
        tickValues = d3
          .range(n)
          .map((i) => d3.quantile(color.domain(), i / (n - 1)));
      }
      if (typeof tickFormat !== "function") {
        tickFormat = d3.format(tickFormat === undefined ? ",f" : tickFormat);
      }
    }
  }

  // Threshold
  else if (color.invertExtent) {
    const thresholds = color.thresholds
      ? color.thresholds() // scaleQuantize
      : color.quantiles
      ? color.quantiles() // scaleQuantile
      : color.domain(); // scaleThreshold

    const thresholdFormat =
      tickFormat === undefined
        ? (d:any) => d
        : typeof tickFormat === "string"
        ? d3.format(tickFormat)
        : tickFormat;

    x = d3
      .scaleLinear()
      .domain([-1, color.range().length - 1])
      .rangeRound([marginLeft, width - marginRight]);

    svg
      .append("g")
      .selectAll("rect")
      .data(color.range())
      .join("rect")
      .attr("x", (d, i) => x(i - 1))
      .attr("y", marginTop)
      .attr("width", (d, i) => x(i) - x(i - 1))
      .attr("height", height - marginTop - marginBottom)
      .attr("fill", (d) => d as any);

    tickValues = d3.range(thresholds.length);
    tickFormat = (i:any) => thresholdFormat(thresholds[i], i);
  }

  // Ordinal
  else {
    x = d3
      .scaleBand()
      .domain(color.domain())
      .rangeRound([marginLeft, width - marginRight]);

    svg
      .append("g")
      .selectAll("rect")
      .data(color.domain())
      .join("rect")
      .attr("x", x)
      .attr("y", marginTop)
      .attr("width", Math.max(0, x.bandwidth() - 1))
      .attr("height", height - marginTop - marginBottom)
      .attr("fill", color);

    tickAdjust = () => {};
  }

  svg
    .append("g")
    .attr("transform", `translate(0,${height - marginBottom})`)
    .call(
      d3
        .axisBottom(x)
        .ticks(ticks, typeof tickFormat === "string" ? tickFormat : undefined)
        .tickFormat(typeof tickFormat === "function" ? tickFormat : undefined)
        .tickSize(tickSize)
        .tickValues(tickValues)
    )
    .call(tickAdjust)
    .call((g) => g.select(".domain").remove())
    .call((g) => {
      g.selectAll("line").attr("stroke", "#fff");
      g.selectAll("text").attr("fill", "#fff");
    })
    .call((g) =>
      g
        .append("text")
        .attr("x", marginLeft)
        .attr("y", marginTop + marginBottom - height - 6)
        .attr("fill", "#fff")
        .attr("text-anchor", "start")
        .attr("font-size", 14)
        .attr("font-weight", "bold")
        .text(title)
    );

  return svg.node();
}

function ramp(color:any, n = 256) {
  var canvas = document.createElement("canvas");
  canvas.width = n;
  canvas.height = 1;
  const context = canvas.getContext("2d");
  if (!context) throw new Error("ctx is not defined");
  for (let i = 0; i < n; ++i) {
    // context.fillStyle = color(i / (n - 1));

    context.fillStyle = color(Number.parseFloat((i / n).toFixed(2)));

    context.fillRect(i, 0, 1, 1);
  }
  return canvas;
}

function removeLegend() {
  d3.select("#palette").selectChildren("*").remove();
}

export { legend, removeLegend };

// d3.scaleSequential([0, 100], d3.interpolateViridis).range(["0","1"])

/**
 * rgb(149, 137, 211), rgb(149, 137, 211), rgb(149, 137, 211), rgb(149, 137, 211), rgb(150, 209, 216), rgb(129, 204, 197), rgb(103, 180, 186), rgb(95, 143, 197), rgb(80, 140, 62), rgb(121, 146, 28), rgb(171, 161, 14), rgb(223, 177, 6), rgb(243, 150, 6), rgb(236, 95, 21), rgb(190, 65, 18), rgb(138, 43, 10), rgb(138, 43, 10)
 */
